Summary
object-observer
provides a deep observation of a changes performed on an object/array graph.
Main aspects:
- implemented via native Proxy (revokable)
- observation is 'deep', yielding changes from a sub-graphs too
- nested objects of the observable graph are observables too
- changes delivered in a synchronous way
- original objects are cloned while turned into
Observable
s - arrays specifics:
- generic object-like mutations supported
- intrinsic
Array
mutation methods supported: pop
, push
, shift
, unshift
, reverse
, sort
, fill
, splice
- massive mutations delivered in a single callback, usually having an array of an atomic changes
- intrinsic mutation methods of
Map
, WeakMap
, Set
, WeakSet
(set
, delete
) etc are not observed (see this issue for more details) - following host objects (and their extensions) are actively checked and NOT cloned / turned into observables:
Date
, Blob
, Error
Support matrix: 61+ | 60+ | 16+ | 8.10.0+
Performance report can be found here
Last versions (full changelog is here)
-
2.9.4
- implemented Issue no. 31 - added option to observe
pathsOf
, direct properties of a specific path only
-
2.8.0
- officially publishing and documenting Issue no. 33 - any nested object of an
Observable
graph is observable in itself
-
2.7.0
- implemented Issue no. 32 - revokation part removed (underlying proxies are still revokable, for any possible future need)
- implemented Issue no. 33 - any nested object of an
Observable
graph is observable in itself - it may be observed/unobserved, it provides relative paths when observed etc.
For a short preview you may want to play with this JSFiddle.
Loading the Library
object-observer
provided as an ES6 module.
In NodeJS
environment, that is not yet fully supporting ES6 modules, use the dedicated distribution as in example below.
Once NodeJS
(presumable 13.11.0 going to LTS) will add a full support for ES6 modules, this special distribution will be removed.
import { Observable } from 'dist/object-observer.min.js';
let Observable = require('./dist/node/object-observer').Observable;
API
Library implements Observable
API as it is defined here.
Examples
Objects
let order = { type: 'book', pid: 102, ammount: 5, remark: 'remove me' },
observableOrder = Observable.from(order);
observableOrder.observe(changes => {
changes.forEach(change => {
console.log(change);
});
});
observableOrder.ammount = 7;
observableOrder.address = {
street: 'Str 75',
apt: 29
};
observableOrder.address.apt = 30;
delete observableOrder.remark;
Arrays
let a = [ 1, 2, 3, 4, 5 ],
observableA = Observable.from(a);
observableA.observe(changes => {
changes.forEach(change => {
console.log(change);
});
});
observableA.pop();
observableA.push('a', 'b');
observableA.shift();
observableA.unshift('x', 'y');
observableA.reverse();
observableA.sort();
observableA.fill(0, 0, 1);
observableA.splice(0, 1, 'x', 'y');
let customer = { orders: [ ... ] },
oCustomer = Observable.from(customer);
oCustomer.orders.sort();
oCustomer.orders.reverse();
Arrays notes: Some of array operations are effectively moving/reindexing the whole array (shift, unshift, splice, reverse, sort).
In cases of massive changes touching presumably the whole array I took a pessimistic approach with a special non-detailed events: 'reverse' for reverse
, 'shuffle' for sort
. The rest of these methods I'm handling in an optimistic way delivering the changes that are directly related to the method invocation, while leaving out the implicit outcomes like reindexing of the rest of the Array.
Observation options
let user = {
firstName: 'Aya',
lastName: 'Guller',
address: {
city: 'of mountaineers',
street: 'of the top ridges',
block: 123,
extra: {
data: {}
}
}
},
oUser = Observable.from(user);
oUser.observe(changes => {}, {path: 'firstName'});
oUser.observe(changes => {}, {path: 'address.city'});
oUser.observe(changes => {}, {pathsOf: 'address'});
oUser.observe(changes => {}, {pathsFrom: 'address'});